#pragma once
#include "feMvOps.h"
#include "feVector.h"
#include "feCache.h"
#include "feSmartPtr.h"
#include "../mfMesh.h"

class mfMesh;
class feMesh;

#define MAX_LAYERS_COUNT 10000

class feMeshFunction
{
public:
	struct Operation
	{
		virtual feSharedPtr<feMeshFunction> operator() (feMeshFunction *iFunction) = 0;
	};

public:
	virtual void	  AddLayer(double iTime, feVector *iLayer) = 0;
	virtual feMesh*   GetMesh() = 0;
	virtual void	  ReserveLayers(int iCount) = 0;
	virtual double	  GetTime(int iIndex) = 0;
	virtual int		  LayersCount() = 0;
	virtual double	  GetU(fePoint *iP, double iTime, int *iLookUp = 0) = 0;
	virtual double	  GetU(int iNode, double iTime) = 0;
	virtual double	  GetU(fePoint *iP, int iLayerNum, int *iLookUp = 0) = 0;
	virtual double	  GetU(int iNode, int iLayerNum) = 0;
	virtual double	  GetDuDt(fePoint *iP, double iTime) = 0;
	virtual double	  GetDuDt(fePoint *iP, int iLayerNum) = 0;
	virtual double	  GetDuDt(int iNode, double iTime) = 0;
	virtual double	  GetDuDt(int iNode, int iLayerNum) = 0;
	virtual double	  GetDuDx(fePoint *iP, double iTime) = 0;
	virtual double	  GetDuDx(int iNode, double iTime) = 0;
	virtual double	  GetDuDx(fePoint *iP, int iLayerNum) = 0;
	virtual double	  GetDuDx(int iNode, int iLayerNum) = 0;
	virtual feVector* GetLayer(int iIndex) = 0;
	virtual bool	  SaveToFile(char *iFileName, char *iMeshFileName) = 0;
	virtual bool	  LoadFromFile(char *iFileName) = 0;
	virtual void	  Clear() = 0;

	virtual ~feMeshFunction() {}
};


//----------------------------------------------------------------------------------------------------------------------------------------
class feSimple1dMeshFunction : public feMeshFunction
{
	struct Layer
	{
		feReal   Time;
		feVector Func;

		//-----------------------------------------------------------------------------
		Layer() { Time = 0; }

		//-----------------------------------------------------------------------------
		Layer(double iT, feVector *iF)
		{
			Time = iT;
			if(iF)
			{
				Func.Allocate(iF->GetSize());
				feMvOps::CopyVV(iF, &Func);
			}
		}


		//-----------------------------------------------------------------------------
		Layer(const Layer &iO) 
		{ 
			Time = iO.Time; 
			Func = iO.Func; 
		}
	};

	Layer*	Layers[MAX_LAYERS_COUNT];
	int		ActLayersCount;
	feSharedPtr<mfMesh>	  Mesh;
	feSharedPtr<feCache<int, 9>> CacheInstance;
	feSharedPtr<feCache<int, 9>> CacheInUse;

protected:
	int	   GetUindex(fePoint *iP, int iStartIndex);
	int	   GetTindex(double iTime, int iStartIndex);

public:
	struct DiffDtOperation : public feMeshFunction::Operation
	{
		virtual feSharedPtr<feMeshFunction> operator() (feMeshFunction *iFunction);
	};

	struct SubOperation
	{
		feSharedPtr<feMeshFunction> operator() (feMeshFunction *iFunction1, feMeshFunction *iFunction2);
	};
	
	struct DiffDxOperation : public feMeshFunction::Operation
	{
		virtual feSharedPtr<feMeshFunction> operator() (feMeshFunction *iFunction);
	};

	struct IntDtOperation : public feMeshFunction::Operation
	{
		virtual feSharedPtr<feMeshFunction> operator() (feMeshFunction *iFunction);
	};
	
	friend struct DiffDtOperation;

public:
	feSimple1dMeshFunction(feSharedPtr<mfMesh>	iMesh)		{ Mesh = iMesh; ActLayersCount = 0; ZeroMemory(Layers, sizeof(Layers[0])*MAX_LAYERS_COUNT); CacheInstance = new feCache<int, 9>(); CacheInUse = CacheInstance; }
	
			void		CopyCacheFrom(feSimple1dMeshFunction *iF) { if(CacheInstance && iF->CacheInstance) { delete CacheInstance; CacheInstance=0; CacheInUse = iF->CacheInstance;} }
	virtual feMesh*		GetMesh()							{ return (feMesh*)((mfMesh*)Mesh); }
	virtual void		AddLayer(double iTime, feVector *iLayer);
	virtual void	    ReserveLayers(int iCount)			{  }
	virtual double		GetTime(int iIndex)					{ return Layers[iIndex]->Time; }
	virtual int			LayersCount()						{ return ActLayersCount; }
	virtual double		GetU(fePoint *iP, double iTime, int *iLookUp = 0);
	virtual double		GetU(int iNode, double iTime);
	virtual double		GetU(fePoint *iP, int iLayerNum, int *iLookUp = 0);
	virtual double		GetU(int iNode, int iLayerNum);
	virtual double		GetDuDt(fePoint *iP, double iTime);
	virtual double		GetDuDt(int iNode, double iTime);
	virtual double		GetDuDt(fePoint *iP, int iLayerNum);
	virtual double		GetDuDt(int iNode, int iLayerNum);
	virtual double		GetDuDx(fePoint *iP, double iTime);
	virtual double		GetDuDx(int iNode, double iTime);
	virtual double		GetDuDx(fePoint *iP, int iLayerNum);
	virtual double		GetDuDx(int iNode, int iLayerNum);
	virtual feVector*	GetLayer(int iIndex)				{ return &Layers[iIndex]->Func;}
	virtual void		Clear();
	virtual bool		SaveToFile(char *iFileName, char *iMeshFileName);
	virtual bool		LoadFromFile(char *iFileName);
	~feSimple1dMeshFunction()								{  Clear(); }
};